<script setup lang="ts">
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import type { UploadFile } from 'element-plus'
import { Parser } from 'node-sql-parser'

const sqlContent = ref('')
const batchSize = ref(1000)
const tableName = ref('')
const fileInput = ref<HTMLInputElement | null>(null)
const parser = new Parser()

// 处理文件上传
const handleFileUpload = (uploadFile: UploadFile) => {
  const file = uploadFile.raw
  
  if (!file) {
    ElMessage.warning('请选择文件')
    return
  }

  const reader = new FileReader()
  reader.onload = (e) => {
    sqlContent.value = e.target?.result as string
  }
  reader.readAsText(file)
}

// 处理文件下载
const handleDownload = () => {
  if (!sqlContent.value.trim()) {
    ElMessage.warning('请先输入或上传SQL内容')
    return
  }

  if (!tableName.value.trim()) {
    ElMessage.warning('请输入目标表名')
    return
  }

  try {
    const convertedSQL = convertToMultiInsert(sqlContent.value, batchSize.value)
    const blob = new Blob([convertedSQL], { type: 'text/plain' })
    const url = window.URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.download = 'batch_insert.sql'
    document.body.appendChild(a)
    a.click()
    document.body.removeChild(a)
    window.URL.revokeObjectURL(url)
    ElMessage.success('文件已下载')
  } catch (error) {
    ElMessage.error('转换失败：' + (error as Error).message)
  }
}

// 转换SQL为批量插入
const convertToMultiInsert = (sql: string, size: number): string => {
  // 分割多行SQL
  const lines = sql.split('\n').filter(line => line.trim())
  
  if (lines.length === 0) {
    throw new Error('没有找到有效的SQL语句')
  }

  try {
    // 解析第一条SQL获取列信息
    const firstAst = parser.astify(lines[0])
    if (!Array.isArray(firstAst)) {
      throw new Error('SQL解析失败')
    }

    const firstStatement = firstAst[0]
    if (firstStatement.type !== 'insert') {
      throw new Error('只支持INSERT语句')
    }

    // 从第一条语句中提取列名
    let columns = []
    if (firstStatement.columns && Array.isArray(firstStatement.columns)) {
      columns = firstStatement.columns.map((col: any) => {
        if (typeof col === 'string') {
          return col
        } else if (col.column) {
          return col.column
        } else if (col.expr && col.expr.column) {
          return col.expr.column
        }
        throw new Error('无法解析列名')
      })
    } else {
      // 如果没有显式的列名，尝试从第一条记录的值中推断列数
      const firstRecord = firstStatement.values[0]
      if (!firstRecord || !Array.isArray(firstRecord.value)) {
        throw new Error('无法获取列信息')
      }
      // 从原始SQL中提取列名
      const columnsMatch = lines[0].match(/insert\s+into\s+[^\s(]+\s*\(([\s\S]+?)\)\s*values/i)
      if (!columnsMatch) {
        throw new Error('无法从SQL中提取列名')
      }
      columns = columnsMatch[1].split(',').map(col => col.trim())
    }

    if (columns.length === 0) {
      throw new Error('未找到列名信息')
    }

    const columnsStr = columns.join(', ')
    
    // 收集所有值
    const values = lines.map(line => {
      try {
        const ast = parser.astify(line)
        if (Array.isArray(ast) && ast[0].type === 'insert') {
          return ast[0].values[0].value.map((v: any) => {
            if (v.type === 'null') {
              return 'NULL'
            } else if (v.type === 'number') {
              return v.value
            } else if (v.type === 'bool') {
              return v.value
            } else {
              // 对所有非数字类型的值添加引号并处理特殊字符
              const strValue = String(v.value).replace(/'/g, "''")
              return `'${strValue}'`
            }
          }).join(', ')
        }
        throw new Error('无效的INSERT语句')
      } catch (e) {
        throw new Error(`SQL语句解析失败: ${line}`)
      }
    })

    if (values.length === 0) {
      throw new Error('没有找到有效的INSERT语句')
    }

    // 按批次大小分组
    const batches = []
    for (let i = 0; i < values.length; i += size) {
      const batch = values.slice(i, i + size)
      batches.push(
        `INSERT INTO ${tableName.value} (${columnsStr}) VALUES\n  (${batch.join('),\n  (')});`
      )
    }

    return batches.join('\n\n')
  } catch (error) {
    throw new Error('SQL解析失败：' + (error as Error).message)
  }
}
</script>

<template>
  <div class="batch-insert-container">
    <div class="header">
      <h2>SQL批量插入转换工具</h2>
      <p class="description">将多行INSERT语句转换为批量INSERT语句，提高执行效率</p>
    </div>

    <div class="controls">
      <div class="upload-section">
        <el-upload
          class="upload-demo"
          action=""
          :auto-upload="false"
          :show-file-list="false"
          accept=".sql,.txt"
          @change="handleFileUpload"
        >
          <el-button type="primary">
            <i class="el-icon-upload" /> 上传文件
          </el-button>
        </el-upload>
      </div>

      <div class="table-name">
        <span class="label">目标表名：</span>
        <el-input
          v-model="tableName"
          placeholder="请输入表名"
          style="width: 200px"
        />
      </div>

      <div class="batch-size">
        <span class="label">批次大小：</span>
        <el-input-number
          v-model="batchSize"
          :min="1"
          :max="5000"
          :step="100"
          step-strictly
        />
      </div>

      <el-button type="success" @click="handleDownload">
        <i class="el-icon-download" /> 下载转换结果
      </el-button>
    </div>

    <div class="content">
      <el-input
        v-model="sqlContent"
        type="textarea"
        :rows="15"
        placeholder="请输入或上传SQL语句，每行一条INSERT语句"
      />
    </div>

    <div class="tips">
      <h3>使用说明：</h3>
      <ol>
        <li>您可以直接在文本框中粘贴SQL语句，或者通过上传文件导入</li>
        <li>每行应该是一个完整的INSERT语句</li>
        <li>请在"目标表名"输入框中指定要插入的表名</li>
        <li>可以通过批次大小控制每个批量插入语句包含的数据条数</li>
        <li>点击下载按钮获取转换后的SQL文件</li>
      </ol>
    </div>
  </div>
</template>

<style scoped>
.batch-insert-container {
  padding: 24px;
  max-width: 1200px;
  margin: 0 auto;
}

.header {
  margin-bottom: 24px;
  text-align: center;
}

.header h2 {
  margin: 0;
  color: #303133;
  font-size: 24px;
}

.description {
  color: #606266;
  margin: 8px 0 0;
}

.controls {
  display: flex;
  gap: 16px;
  align-items: center;
  margin-bottom: 24px;
  padding: 16px;
  background: #f8f9fa;
  border-radius: 8px;
}

.upload-section {
  flex: 1;
}

.table-name {
  display: flex;
  align-items: center;
  gap: 8px;
}

.batch-size {
  display: flex;
  align-items: center;
  gap: 8px;
}

.label {
  color: #606266;
}

.content {
  margin-bottom: 24px;
}

.tips {
  background: #f8f9fa;
  padding: 16px;
  border-radius: 8px;
}

.tips h3 {
  margin: 0 0 12px;
  color: #303133;
  font-size: 16px;
}

.tips ol {
  margin: 0;
  padding-left: 20px;
  color: #606266;
}

.tips li {
  margin-bottom: 8px;
}

.tips li:last-child {
  margin-bottom: 0;
}
</style> 